﻿/*
 * Game.cs
 * 
 * Purpose:
 * Data structure representing a game played in EAT.
 * 
 * Revision Log:
 * 08-SEP-12, C. Bush, First Draft.
 * 12-SEP-12, Q.Nguyen
 */

using System;
using System.Threading;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Runtime.Serialization;
using Air_Trafic_Control.Domain.Common;
using Air_Trafic_Control.Domain.Control;
using Air_Trafic_Control.Domain.Util;
using System.Runtime.Remoting.Lifetime;

namespace Air_Trafic_Control.Domain.Entity
{
    [Serializable()]
    public class Game
    {
        [NonSerialized()]
        private RADAR m_radar;

        private string m_UserName = "";
        private int m_difficultyLevel = Constants.GAME_MIN_DIFFICULTY;
        private int m_numberLanded = 0;
        private int m_numberCrashed = 0;// the number crashed or that left the screen without landing
        private int m_ObjProductRate = 2000;// in milliseconds (hard code for now)
        private List<FlyObject> m_flyingObjects = new List<FlyObject>();
        private bool m_timerEnabled;
        private double m_RunTime;

        [NonSerialized()]
        private FlyObject m_selectedFlyingObject = null;

        [NonSerialized()]
        private Random m_randomizer;

        [NonSerialized()]
        private Timer m_GameTimer;

        private static Logger log = new Logger(typeof(Game));


#region Construtors

        /// <summary>
        /// Parameterless constructor required for serialization.
        /// </summary>
        public Game()
        {
            this.m_GameTimer = new Timer(new TimerCallback(GenerateFlyObj));
            this.m_radar = new RADAR(10, m_ObjProductRate, Constants.SYS_RADAR_DIAMETER, Constants.SYS_AIRPORT_DIAMETER, this);
            this.m_randomizer = new Random();
            m_GameTimer.Change(Timeout.Infinite, Timeout.Infinite);
        }

        /// <summary>
        /// Override constructor
        /// </summary>
        /// <param name="name">The name to be used for this game. This must be unique to the current player.</param>
        public Game(string name)
        {
            this.m_GameTimer = new Timer(new TimerCallback(GenerateFlyObj));
            this.m_UserName = name;
            this.m_radar = new RADAR(10, m_ObjProductRate, Constants.SYS_RADAR_DIAMETER, Constants.SYS_AIRPORT_DIAMETER, this);
            this.m_randomizer = new Random();
            m_GameTimer.Change(Timeout.Infinite, Timeout.Infinite);
        } 

#endregion

#region Properties

        /// <summary>
        /// Returns the Game's Name.
        /// </summary>
        public string Name
        {
            get
            {
                return this.m_UserName;
            }
            set
            {
                this.m_UserName = value;
            }
        }

        /// <summary>
        /// The difficulty level of this game. It must range between Constants.GAME_MIN_DIFFICULTY and Constants.GAME_MAX_DIFFICULTY.
        /// </summary>
        public int DifficultyLevel
        {
            get
            {
                return this.m_difficultyLevel;
            }
            set
            {
                if (value < Constants.GAME_MIN_DIFFICULTY)
                {
                    this.m_difficultyLevel = Constants.GAME_MIN_DIFFICULTY;
                }
                else if (value > Constants.GAME_MAX_DIFFICULTY)
                {
                    this.m_difficultyLevel = Constants.GAME_MAX_DIFFICULTY;
                }
                else
                {
                    this.m_difficultyLevel = value;
                }
            }
        }

        /// <summary>
        /// The number of planes landed for this game so far.
        /// </summary>
        public int NumberLanded
        {
            get
            {
                return this.m_numberLanded;
            }
            set
            {
                this.m_numberLanded = value;
            }
        }

        /// <summary>
        /// The time elapsed so far.
        /// </summary>
        public int FlyObjProductRate
        {
            get
            {
                return this.m_ObjProductRate;
            }
            set
            {
                this.m_ObjProductRate = value;
            }
        }

        /// <summary>
        /// The number of planes crashed so far.
        /// </summary>
        public int NumberCrashed
        {
            get
            {
                return this.m_numberCrashed;
            }
            set
            {
                this.m_numberCrashed = value;
            }
        }

        /// <summary>
        /// The RADAR for this game.
        /// </summary>
        public RADAR Radar
        {
            get
            {
                return this.m_radar;
            }
        }

        /// <summary>
        /// Stores the currently selected flying object.
        /// </summary>
        public FlyObject SelectedFlyingObject
        {
            get
            {
                return this.m_selectedFlyingObject;
            }
            set
            {
                this.m_selectedFlyingObject = value;
            }
        }

        /// <summary>
        /// The flying objects generated by game
        /// </summary>
        public List<FlyObject> FlyingObjs
        {
            get { return this.m_flyingObjects; }
        }

        public Boolean GameTimerEnable
        {
            get { return this.m_timerEnabled; }
            set
            {
                if (value)
                {
                    this.m_timerEnabled = true;
                    this.m_GameTimer.Change(0, this.m_ObjProductRate);
                }
                else
                {
                    this.m_timerEnabled = false;
                    this.m_GameTimer.Change(Timeout.Infinite, Timeout.Infinite);
                }
            }
        }

        public double RunTime
        {
            get
            {
                return this.m_RunTime;
            }
            set
            {
                this.m_RunTime = value;
            }
        }

#endregion

#region Public Methods

        /// <summary>
        /// Override the C# ToString method so we can add this directly to a ComboBox's Items collection
        /// </summary>
        /// <returns>The Name of the Game.</returns>
        public override string ToString()
        {
            return this.m_UserName;
        }

        public void CleanupFlyObjList()
        {
            lock (m_flyingObjects)
            {
                if (m_flyingObjects != null)
                {
                   //start at the end and reverse iterate...
                   int size = m_flyingObjects.Count - 1;
                    for (int i = size; i >= 0; i--)
                    {
                        FlyObject aircraft = m_flyingObjects[i] as FlyObject;

                        double distance = Formulas.DistanceToCenter(aircraft.OwnPosition.X, aircraft.OwnPosition.Y);
                        if (distance <= 50 || distance >= (Constants.SYS_RADAR_RADIUS + 10))
                        {
                           CheckCrashOrLanded(distance);
                           if (aircraft.IsSelected == true)
                           {
                              this.SelectedFlyingObject = null;
                           }
                            m_flyingObjects.Remove(aircraft);
                            Game.log.Info("Aircraft Removed.");
                        }
                    }
                }
            }
        }




        public void OnStartButton(bool OnOff)
        {
            foreach (FlyObject fObj in m_flyingObjects)
            {
                fObj.FlyObjTimerEnable = OnOff;
            }
        }

#endregion

#region Private Methods

        /// <summary>
        /// Timer event handler to create an aircraft every tick
        /// </summary>
        private void GenerateFlyObj(object state)
        {
            Type FlyObjType = RandomlyCreateType();
            if (FlyObjType != null && this.FlyingObjs.Count < this.m_radar.MaxFlyObjs)
            {
                if (FlyObjType.FullName.Equals(typeof(C130).FullName))
                {
                    C130 aircraft = new C130();
                    aircraft.FlyObjTypeName = FlyObjType.Name;
                    aircraft.FlyObjTimerEnable = true;
                    lock (m_flyingObjects)
                    {
                        m_flyingObjects.Add(aircraft);
                    }
                }
                if (FlyObjType.FullName.Equals(typeof(F15).FullName))
                {
                    F15 aircraft = new F15();
                    aircraft.FlyObjTypeName = FlyObjType.Name;
                    aircraft.FlyObjTimerEnable = true;
                    lock (m_flyingObjects)
                    {
                        m_flyingObjects.Add(aircraft);
                    }
                }
                if (FlyObjType.FullName.Equals(typeof(UFO).FullName))
                {
                    UFO aircraft = new UFO();
                    aircraft.FlyObjTypeName = FlyObjType.Name;
                    aircraft.FlyObjTimerEnable = true;
                    lock (m_flyingObjects)
                    {
                        m_flyingObjects.Add(aircraft);
                    }
                }

            }
        }

        private Type RandomlyCreateType()
        {
            Type FlyObjType = null;
            int TypeNum = m_randomizer.Next(1, 4);

            switch (TypeNum)
            {
                case (1):
                    FlyObjType = typeof(UFO);
                    break;
                case (2):
                    FlyObjType = typeof(C130);
                    break;
                case (3):
                    FlyObjType = typeof(F15);
                    break;
            }
            return FlyObjType;
        }

        

#endregion

        public void CheckCrashOrLanded(double distance)
        {
           if (distance <= 50)
           {
              this.NumberLanded++;
           }
           else 
              this.NumberCrashed++;
        }
    }
}
